package com.itheima.springboot_reggie.web;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.springboot_reggie.common.R;
import com.itheima.springboot_reggie.dto.DishDTO;
import com.itheima.springboot_reggie.emtity.Category;
import com.itheima.springboot_reggie.emtity.Dish;
import com.itheima.springboot_reggie.emtity.DishFlavor;
import com.itheima.springboot_reggie.emtity.Setmeal;
import com.itheima.springboot_reggie.service.ICategoryService;
import com.itheima.springboot_reggie.service.IDishFlavorService;
import com.itheima.springboot_reggie.service.IDishService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@RestController
@RequestMapping("/dish") //请求url地址：
public class DishController {

    @Autowired
    IDishService dishService;

    @Autowired
    ICategoryService categoryService;

    @Autowired
    IDishFlavorService dishFlavorService;

    /**    新增菜品功能开发
     *  接口1：查询菜品分类列表数据*
     *  请求url地址：/category/list ?type=1
     *  怎么又回到category类呢？因为在新增页面里有个菜品分类的查询展示框（菜品类查询和套餐类查询）
     *   因此将新增写到  categoryController 中
     */


    /**        添加菜品【复杂】
     * 备注：涉及表 Dish【菜品表】、Dish_flavor【菜品口味表】
     * 请求url地址：/dish
     * 请求方式：POST
     * 请求参数：JSON格式数据 ---> 用什么对象封装  Dish  DishFlavor 都不是！用新的实体类 DishDto;
     *         表现层接收待添加的菜品数据时，需要使用dishDTO来接收，不然dish拿不到菜品口味数据的
     * 响应结果【格式统一】：R 对象
     */
    @PostMapping  //在类上已经写了请求前缀，就不需要再写 请求url地址：/dish,没有路径
    public R addDish(@RequestBody DishDTO dishDTO){
        /**  此时浏览器页面上的菜品表Dish的添加还涉及到菜品口味表Dish_flavor
         * 当前的entity和持久层、数据表交互，dish对于表现层满足不了需求，所以我们新建了DishDTO
         * DishDTO里面只声明了一个categoryName还不够，还需要加上口味,先复制实体类 DishFlavor到entity包下
         * 然后到 DishDTO类中 再声明下DishFlavor口味的属性 private List <DishFlavor> flavors;
         */
        log.info("add dishDTO:{}",dishDTO);

        //添加dish表数据
        dishService.save(dishDTO);

        //添加dishflavors表数据 ,用集合去接收
        List<DishFlavor> flavors = dishDTO.getFlavors();

       //判断是否选择了口味
        if (flavors!=null && flavors.size()>0){
            //保存菜品口味数据到菜品口味表dish_flavor，需要注意：DishFlavor中dishID是没有数据的，
            //所以要手动设置
            Long dishId = dishDTO.getId();//菜品id
            for (DishFlavor flavor : flavors) {
                flavor.setDishId(dishId);
               dishService.getById(flavor);
            }
        }

        return R.success("添加成功");
    }




    /**     接口：分页+条件查询 菜品数据
     *
     * 注意点： 封装的结果数据需要封装dishDTO,因为dish里面缺少categoryName,缺少前台就拿不到分类名称
     *
     * 请求url地址：/dish/page ?page=1&pageSize=10&name=白菜
     * 请求方式：GET
     * 请求参数：?page=1&pageSize=10&name=白菜
     * 响应结果【格式统一】：R 对象
     */
    @GetMapping("/page")
    public R<Page> queryPage(Integer page, Integer pageSize, String name){
        //目前的dish表中只有category_id,没有category_name（分类名称）
//        Page<Dish> dishPage = new Page<>(page,pageSize);
        /**此时，需要DTO对象，我们现在写的叫entity实体对象和数据表一一对应的，数据表有哪些字段，实体类就有，
         * 而有时候我们表现层和前端、客户、业务层进行交流、交互时，往往实体对象里面的数据满足不了我们的需求(比如现在就没有category_name)
         * entity跟我们持久层的数据表绑定起来的，未来跟持久层交互起来非常方便
         * 此时，可以再封装一个DTO对象 ，跟表现层、业务层、前端进行交互时时用的实体对象，它可以多封装些数据
         *为了不去影响持久层的entity操作，所以新建一个DTO类，新建一个包dto,实体类为DishDTO，专门给表现层使用的
         */

        // 创建Dish分页对象，这个Dish分页对象是用来查询dish菜品分类分页数据的
        Page<Dish> dishPage = new Page<>(page,pageSize);
        //创建条件构造器
        LambdaQueryWrapper<Dish> wrapper = new LambdaQueryWrapper<>();
        //设置查询条件
//        wrapper.like(name!=null && name!="",Dish::getName,name);
        //选择StringUtils org.apache.commons.lang 这个依赖（提供了大量的字符串操作的API）
        // 做非空判断，可以利用工具类判断
        wrapper.like(StringUtils.isNotEmpty(name),Dish::getName,name);
        //也可以根据需求添加条件， 比如按照发布时间降序
        wrapper.orderByDesc(Dish::getCreateTime);

        dishService.page(dishPage,wrapper);//要加条件，因为参数当中还有个name

        /** 现在运行下，打开浏览器查询菜品管理，菜品分类没有，怎么把上面封装了Dish的page转换成封装DishDTO的page呢？
         *  即，把上面的dishPage赋值给下面的dishDTOPage，上面的里面有东西，而下面的没有，现在里面是空的
         *   可以这样：  /*dishDTOPage.setTotal(dishPage.getTotal());  Total总条数    Records记录数
         *              dishDTOPage.setRecords(dishPage.getRecords()); 这样一个一个设置麻烦、复杂
         */

        //现在有两个分页对象，最终要返回的是DishDTO这个分页对象给前端，因为 dishDTO不仅有Dish菜品种类还有categoryName菜品名称
        Page<DishDTO> dishDtoPage = new Page<DishDTO>();

        //实现 Page<Dish> 和 Page<DishDTO> 对象 之间的属性对拷（拷贝），利用工具类 BeanUtils org.springframework.beans
        //copyProperties()这个方法意思是：给我一个源对象，我把源对象里面的所有属性都拷给目标对象
        //copyProperties()这个方法有三个参数的，其中有个参数是 可以忽略、不考虑什么属性的意思
        BeanUtils.copyProperties(dishPage,dishDtoPage,"records"); //记录数
        //现在我们将 dishPage里面的属性都给dishDTOPage，但就是不拷"records",因为这两个对象records里面封装的东西不一样
        //但现在 records 没有数据，totals是有值的

      /*  //写法一：直接for循环，找到dishPage里面查到的Records记录数（刚刚进行分页查询时查到的数据都封装到dishPage里面）
        List<DishDTO> dishDTOList = new ArrayList<>();
        //dishPage.getRecords().for
        for (Dish dish : dishPage.getRecords()) {//Dish里查到了23条数据，进行循环遍历下，封装成23条DishDTO添加到集合里
            DishDTO dishDTO = new DishDTO();
            dishDTOList.add(dishDTO);
           *//* dishDTO.setId(dish.getId());
            dishDTO.setCode(dish.getCode());
            dishDTO.setName(dish.getName());
            ........
            *//*
            //直接用工具类获取
            BeanUtils.copyProperties(dish,dishDTO); //拷贝
            //查询菜品分类表，查询当前这道菜的分类名称： select categoryName from category where id=?(dish表中的category_id)

            Category category = categoryService.getById(dish.getCategoryId());//分类
            dishDTO.setCategoryName(category.getName());//设置
            dishDTOList.add(dishDTO);
        }*/

        // 写法二： 使用stream流，借助map接口

        List<DishDTO> dishDTOList = dishPage.getRecords().stream().map(dish -> {
            DishDTO dishDTO = new DishDTO();
            //直接用工具类获取
            BeanUtils.copyProperties(dish,dishDTO); //拷贝
            //查询菜品分类表，查询当前这道菜的分类名称： select categoryName from category where id=?(dish表中的category_id)

            Category category = categoryService.getById(dish.getCategoryId());//分类
            dishDTO.setCategoryName(category.getName()); //设置
            return dishDTO;   //返回
        }).collect(Collectors.toList());

        //将处理完毕后的dishDTO集合封装到Page对象中返回
        dishDtoPage.setRecords(dishDTOList);

        return R.success(dishDtoPage);
    }

    /**       修改菜品 ： 先查后改
     *    根据id查询菜品信息和对应的口味信息
     *    注意点： 前端要返回dishDTO，它不仅要展示待修改的菜品数据，口味也要
     *   根据ID查询菜品信息
     *
     *   最终返回的是DishDTO,封装的也是它
     */
    @GetMapping("/{id}")  //id若不加{}占位符，就是请求路径，加了{id}就是请求参数，比如网页上where id = 几
    public R <DishDTO> queryById(@PathVariable Long id){

       //方法一：
        /**
         * 1. 在DishService接口中扩展getByIdWithFlavor方法
         * 2. 在DishService实现类中实现此方法：
         *         具体逻辑为:
         *          A. 根据ID查询菜品的基本信息
         *          B. 根据菜品的ID查询菜品口味列表数据
         *          C. 组装数据并返回
         * 3. 在DishController中创建get方法
         *
         */
        DishDTO dishDTO = dishService.getByIdWithFlavor(id);
        return R.success(dishDTO);

       //方法二：

        //1.查询dish表
        //  DishDTO dishDto = (DishDTO) dishService.getById(id);

        //2.查询口味表 dish_flavor
    }

    /**
     *  修改菜品
     *  请求url地址：/dish
     * 请求方式：PUT
     * 请求参数：JSON格式的数据  ，使用DishDto封装,
     * 注意点： 使用DishDto来接收待修改的菜品和口味
     */
    @PutMapping
    public R updateDish(@RequestBody DishDTO dishDTO ){
//分析：要先删除后添加:不这样做，未来要在现有的口味上改，有可能改，有可能加，有可能改一个，也有可能改
 // 改多个数据 ，很麻烦，所以上来直接把当前的口味直接删掉，然后再统一加，批量添加口味
      //方法一：
        //1.修改dish表中指定的字段


        //2.删除dish关联的口味表中当前菜品的口味数据

        //3.批量添加此次请求发送的口味列表数据

     //方法二：
        /**  1.在DishService接口中扩展方法updateWithFlavor
         *       //更新菜品信息，同时更新对应的口味信息
         *              public void updateWithFlavor(DishDto dishDto);
         *   2.在DishServiceImpl中实现方法updateWithFlavor
         *   3.在DishController中创建update方法
          */
        dishService.updateWithFlavor(dishDTO);

        return R.success("修改菜品成功");
    }

    /**
     *  删除菜品
     * 接口六：删除菜品接口[和批量删除用的是同一个接口]
     * 请求url地址：/dish    ?ids=1504708761831292929,1504653856504483842
     * 请求方式：DELETE
     * 请求参数：?ids=1504708761831292929,1504653856504483842
     * 响应结果【格式统一】：R 对象
     *
     *    使用mybatis-plus提供的逻辑删除！  1：删除  ， 0 ： 不删除
     */
    @DeleteMapping
    public R deleteByIds(@RequestParam List ids){  // k=v 格式 ,用list集合接收ids

        //逻辑删除
        dishService.removeByIds(ids);


        return R.success("删除成功");
    }

    /**
     *  接口七：   起售/停售 批量起售/批量停售   批量更新
     * 请求url地址：/dish/status/1 或者是0 ?ids=1504708761831292929,1504653856504483842,1413384757047271425
     * 请求方式：POST
     * 请求参数：?ids=1504708761831292929,1504653856504483842,1413384757047271425
     * 响应结果【格式统一】：R 对象
     */
    //批量更新
    @PostMapping("/status/{status}")   //第二个status是变量要用占位符{status}去接收
    public R<DishDTO> changStatus(@RequestParam List ids,@PathVariable Integer status){
        // update dish set status = ? where id in (1,2,3)
        //创建条件构造器
        LambdaUpdateWrapper<Dish> wrapper = new LambdaUpdateWrapper<>();

        wrapper.set(Dish::getStatus,status);//update dish set status = ?
        wrapper.in(Dish::getId,ids);   //where id in (1,2,3)
        dishService.update(wrapper);

        return R.success(null);

    }

    /**    新增套餐   根据条件查询对应的菜品数据
     * 在DishController中定义方法list，接收Dish类型的参数：
     *
     * 在查询时，需要根据菜品分类categoryId进行查询，
     * 并且还要限定菜品的状态为起售状态(status为1)，然后对查询的结果进行排序。
     */

    /**
     *  客户端主页面请求此接口
     *        完善的功能： 查询并封装当前每一道菜品的口味数据
     */
    @GetMapping("/list")
    public R<List<DishDTO>> list(Dish dish){
        // 1.分页+条件查询    构造查询条件
        LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(dish.getCategoryId() != null ,Dish::getCategoryId,dish.getCategoryId());

        queryWrapper.eq(Dish::getStatus,1); //添加条件，查询状态为1（起售状态）的菜品

        //添加排序条件
        queryWrapper.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime);

        //2。查询菜品
        List<Dish> list = dishService.list(queryWrapper);

        //3.查询菜品
        List<DishDTO> dishDtoList = list.stream().map((item) -> {
            DishDTO dishDTO = new DishDTO();
            BeanUtils.copyProperties(item,dishDTO);

            // 3.1 封装分类名称
            Long categoryId = item.getCategoryId();//分类id
            //根据id查询分类对象
            Category category = categoryService.getById(categoryId);
            if(category != null){
                String categoryName = category.getName();
                dishDTO.setCategoryName(categoryName);
            }

            //当前菜品的id
            Long dishId = item.getId();
            LambdaQueryWrapper<DishFlavor> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.eq(DishFlavor::getDishId,dishId);
            //SQL:select * from dish_flavor where dish_id = ?
            List<DishFlavor> dishFlavorList = dishFlavorService.list(lambdaQueryWrapper);
            dishDTO.setFlavors(dishFlavorList);

            return dishDTO;
        }).collect(Collectors.toList());

        return R.success(dishDtoList);
    }

}


